﻿using Common; 
using DeviceClientGrain;
using Orleans;
using Orleans.Runtime;
using Orleans.Streams;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Server.Impl
{
    /// <summary>
    /// AGV基本信息和 操作判断
    /// </summary>
    public class AgvImpl : Grain, IAgv
    {
        private readonly IPersistentState<AgvInfo> cache;
        IAsyncStream<MsgData>? steam = null;


        public AgvImpl([PersistentState("agvInfo", "redisStorage")] IPersistentState<AgvInfo> persistent)
        {
            this.cache = persistent;
        }

        public override async Task OnActivateAsync()
        {
            await cache.ReadStateAsync();
            await base.OnActivateAsync();

            var 服务 = base.GetStreamProvider("DeviceProvider");
            steam = 服务.GetStream<MsgData>(Guid.Empty, "AGV");

        }

        public async Task 允许离开机台()
        {

            //注意这里，不同的场景有不同的处理。算法有很多种。
            //比如下道工序的机台不够并且待命点也不够，只能让AGV原地等待，循环的判断可移动
            //也可能退出机台，直接去下个判断点（最理想）
            //建议优先调查设计，提前设计。此处代码就不用写了


            //例，赋值下道工序，并且离开机台，暂时不解锁，离开一段时间后再解
          

            if (string.IsNullOrWhiteSpace(cache.State.DeviceGroup) == false)
            {
                var deviceGrain = GrainFactory.GetGrain<IDeviceGroup>(cache.State.DeviceGroup);
                var 下道工序 = await deviceGrain.获取下道工序(cache.State.DeviceMarkerID);
                //更新工序,不管离开还是未离开 
                cache.State.DeviceGroup = "";
                cache.State.NextGroup = 下道工序.DeviceGroup;
                cache.State.Line = 下道工序.line;
                await cache.WriteStateAsync();
            }
            Agv指令.完成传输(this.GetPrimaryKeyLong(), cache.State.IP);

        }

        /// <summary>
        /// 本Grain调用入口
        /// </summary>
        /// <param name="状态"></param>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        /// <param name="上层满料"></param>
        /// <param name="下层满料"></param>
        /// <param name="IP"></param>
        /// <returns></returns>
        public async Task 处理数据(Agv状态 状态, int X, int Y, bool 上层满料, bool 下层满料, string IP)
        {
            bool hasChange = false;
            #region 更新位置信息
            if ((int)状态 != cache.State.Status || X != cache.State.X || Y != cache.State.Y || 上层满料 != cache.State.UpFull || 下层满料 != cache.State.DownFull)
            {
                hasChange = true;
                cache.State.Status = (int)状态;
                cache.State.X = X;
                cache.State.Y = Y;
                cache.State.UpFull = 上层满料;
                cache.State.DownFull = 下层满料;

                var 信息集合Grain = GrainFactory.GetGrain<IAgvSet>(0);
                _ = 信息集合Grain.更新AGV信息(this.GetPrimaryKeyLong(), 状态, X, Y);
            }

            #endregion

            #region 缓存信息

            //这里需要简单的说明，有些小伙子脑回路神奇，喜欢乱改设备的IP地址
            if (IP != cache.State.IP)
            {
                hasChange = true;
                cache.State.IP = IP;
            }
            if (hasChange)
            {
                await cache.WriteStateAsync();
            }
            #endregion


            //开始处理
            var markerGrain = GrainFactory.GetGrain<IMarkerSet>(0);
            var marker = await markerGrain.获取地标(X, Y);  // 地标为null算正常


            switch (状态)
            {
                case Agv状态.行驶:
                    _ = 行驶中的判断(X, Y);
                    break;
                case Agv状态.停止:
                    //什么都不用做
                    break;
                case Agv状态.交管中:
                    //交管中的AGV，要么由后台指令释放，要么由其它AGV触发
                    //什么都不用做。
                    break;
                case Agv状态.到达:
                    if (marker != null)
                    {
                        _ = 到达(markerGrain, marker);
                    }
                    break;
                case Agv状态.异常:
                    //什么都不用做
                    break;
                case Agv状态.上层传出中:
                    //看情况处理
                    break;
                case Agv状态.下层传出中:
                    //看情况处理
                    break;
                case Agv状态.上层传入中:
                    //看情况处理
                    break;
                case Agv状态.下层传入中:
                    //看情况处理
                    break;
                case Agv状态.上层传出完成:
                    _ = 传输完成(marker, 上层满料, 下层满料);
                    break;
                case Agv状态.下层传出完成:
                    _ = 传输完成(marker, 上层满料, 下层满料); ;
                    break;
                case Agv状态.上层传入完成:
                    _ = 传输完成(marker, 上层满料, 下层满料);
                    break;
                case Agv状态.下层传入完成:
                    _ = 传输完成(marker, 上层满料, 下层满料);
                    break;
                case Agv状态.上入下出中:
                    //看情况处理
                    break;
                case Agv状态.上出下入中:
                    //看情况处理
                    break;
                case Agv状态.上入下出完成:
                    _ = 传输完成(marker, 上层满料, 下层满料);
                    break;
                case Agv状态.上出下入完成:
                    _ = 传输完成(marker, 上层满料, 下层满料);
                    break;
                case Agv状态.可传输:
                    //发送到达
                    _ = 可传输(marker, 上层满料, 下层满料);
                    break;
            }
        }

        public Task 单层传输(bool 上层, bool 收料)
        {
            if(上层 )
            {
                Agv指令.上层传输(this.GetPrimaryKeyLong(), 收料, cache.State.IP);
            }else
            {
                Agv指令.下层传输(this.GetPrimaryKeyLong(), 收料, cache.State.IP);
            }
            if (steam != null)
            { 
                MsgData msg = new MsgData()
                {
                    Cmd = "AGV已就位",
                    DeviceID = cache.State.DeviceID,
                };
                _ = steam.OnNextAsync(msg);
            }

            return Task.CompletedTask;
        }
      

        public Task 一起传输(bool 上收下出)
        {
            if (上收下出==false)
            {
                //上面出料
                Agv指令.上下传输(this.GetPrimaryKeyLong(), true, cache.State.IP);
            }
            else
            {
                //上面收料
                Agv指令.上下传输(this.GetPrimaryKeyLong(), false, cache.State.IP);
            }
            if (steam != null)
            {
                MsgData msg = new MsgData()
                {
                    Cmd = "AGV已就位",
                    DeviceID = cache.State.DeviceID,
                };
                _ = steam.OnNextAsync(msg);
            }
            return Task.CompletedTask;
        }


        #region 内部Task，处理AGV的各种事件
        async Task 行驶中的判断(int X, int Y)
        {
            long AgvID = this.GetPrimaryKeyLong();

            #region 交管区判断
            var areaSetGrain = GrainFactory.GetGrain<IAreaSet>(0);
            var AreaID = await areaSetGrain.是否在区域内(X, Y);

            if (AreaID == 0)
            {
                #region 不在交管区内,但是曾经锁过交管区
                if (cache.State.LockAreaID > 0)
                {
                    //需要释放
                    var areaGrain1 = GrainFactory.GetGrain<IAreaLock>(cache.State.LockAreaID);
                    if (areaGrain1 != null)
                    {
                        _ = areaGrain1.释放区域();
                    }

                    cache.State.LockAreaID = 0;
                    await cache.WriteStateAsync();
                }
                #endregion

                #region 释放占用的传输机台，超过传输机台1.4米即可

                //只释放机台的地标，离开那个位置才释放
                if (string.IsNullOrWhiteSpace(cache.State.DeviceMarkerID) == false)
                {
                    var mg = GrainFactory.GetGrain<IMarkerSet>(0);
                    var deviceMarker = await mg.获取指定地标(cache.State.DeviceMarkerID);
                    if (deviceMarker != null && (Math.Abs(deviceMarker.X - X) > 140 || Math.Abs(deviceMarker.Y - Y) > 140)) //1.4米的半径，AGV上报单位如果是CM
                    {
                        //超过范围释放占用 
                        var 信息集合Grain = GrainFactory.GetGrain<IAgvSet>(0);
                        _ = 信息集合Grain.更新AGV占用传输机台(AgvID, "", "");

                        cache.State.DeviceID = "";
                        cache.State.DeviceMarkerID = "";
                        await cache.WriteStateAsync();
                    }
                }
                #endregion

                #region 如果不在区域内，检测前方区域内是否有其它AGV，如果有，停下来
                /*
                WaringArea? area = GetVirtualArea(receiveData.AgvID);
                if (area == null) return;

                List<AgvData> agvList = await AgvDataSet.GetAgvListAsync(area.MinX, area.MinY, area.MaxX, area.MaxY, receiveData.AgvID);
                if (agvList.Count == 0) return; //前方区域没有其它AGV

                foreach (AgvData agv in agvList)
                {
                    PointF targetPoint = new PointF(agv.X, agv.Y);
                    if (WarningAreaDataSet.IsInPolygon(targetPoint, area.PointList))
                    {
                        //在区域内，当前agv需要停下来
                        byte[] buf = AgvEvent.Traffic(receiveData.AgvID, true);
                        _ = AgvDataSet.SendToAgvAsync(receiveData.AgvID, buf);
                        break;
                    }
                }
                 */
                #endregion
            }
            else
            {
                #region 进入了交管区

                var areaGrain = GrainFactory.GetGrain<IAreaLock>(cache.State.LockAreaID);
                bool 继续行驶 = await areaGrain.AGV进入区域(AgvID);
                if (继续行驶 == false)
                {
                    //需要AGV停下来
                    Agv指令.行驶管控(AgvID, true, cache.State.IP);
                }
                else
                {
                    //锁定了区域，不需要停下来，继续行驶
                    if (cache.State.LockAreaID != AreaID)
                    {
                        cache.State.LockAreaID = AreaID;
                        await cache.WriteStateAsync();
                    }
                }
                #endregion
            }
            #endregion
        }



        /// <summary>
        /// 到达了某个地标
        /// </summary>
        /// <param name="markerSet"></param>
        /// <param name="marker"></param>
        /// <returns></returns>
        async Task 到达(IMarkerSet markerSet, MarkerInfo marker)
        {
            long AgvID = this.GetPrimaryKeyLong(); 
            switch (marker.Type)
            {
                case MarkerEnum.待命:
                    //什么也不用做
                    break;
                case MarkerEnum.传输机台:
                   
                    #region 锁定传输机台
                     
                    //关联关系
                     var 信息集合Grain = GrainFactory.GetGrain<IAgvSet>(0);
                     _ = 信息集合Grain.更新AGV占用传输机台(AgvID, marker.DeviceID, marker.MarkerID);
                    
                    #endregion

                    if (cache.State.DeviceGroup != marker.Group || cache.State.DeviceMarkerID != marker.MarkerID)
                    {
                        //锁定传输机台，包含人工把AGV推了进去
                        cache.State.DeviceGroup = marker.Group;
                        cache.State.DeviceMarkerID = marker.MarkerID;
                        cache.State.DeviceID = marker.DeviceID;
                        await cache.WriteStateAsync();
                    } 
                    //可传输了再给机台发消息
                    break;
                case MarkerEnum.充电:
                    //什么也不用做
                    break;
                case MarkerEnum.传输机台选择:
                    //选传输机台

                    /*
                     * 此处需要自定义一些代码,不同的判断点有不同的策略，空车和有物料的AGV也会有不同的判断逻辑 
                     * 可以按地标进行单独的处理。
                     */

                    //假设有预定的工序 ，不区分空车不空车
                    if(string.IsNullOrWhiteSpace(cache.State.DeviceGroup) && string.IsNullOrWhiteSpace(cache.State.NextGroup))
                    {
                        //初始化，在起点或者本就是空车，啥也没有，不用处理
                    }
                    else
                    {
                        //更新当前工序
                        if(cache.State.DeviceGroup != cache.State.NextGroup)
                        {
                            cache.State.DeviceGroup=cache.State.NextGroup;
                            await cache.WriteStateAsync();
                        }
                    }
                   
                     //当前工序
                    var deviceGrain2 = GrainFactory.GetGrain<IDeviceGroup>(cache.State.DeviceGroup);
                    var 可用机台列表 = await deviceGrain2.选择可用传输机台地标(cache.State.Line, null);
                    if (可用机台列表.Count > 0)
                    {
                        var iaGrain = GrainFactory.GetGrain<IAgvSet>(0);
                        可用机台列表 = await iaGrain.过滤占用机台(可用机台列表);
                    }

                    if (可用机台列表.Count > 0)
                    {
                        var deviceMarkerID = 可用机台列表[0]; //取第一个就好了
                        var mkGrain=GrainFactory.GetGrain<IMarkerSet>(0);
                        var 机台marker = await mkGrain.获取指定地标(deviceMarkerID);
                        if (机台marker != null)
                        { 
                            #region 有传输机台可以用，让AGV过去

                            var 入口地标ID = await markerSet.获取对应地标号(deviceMarkerID);
                            var routeGrain = GrainFactory.GetGrain<IRoute>(0);
                            var route = await routeGrain.获取两个地标之间的路由(marker.MarkerID, 入口地标ID);
                            if (route > 0  )
                            {
                                var agvSetGrain = GrainFactory.GetGrain<IAgvSet>(0);
                                _ = agvSetGrain.更新AGV占用传输机台(AgvID, 机台marker.DeviceID, 机台marker.MarkerID);

                                cache.State.DeviceMarkerID = 机台marker.MarkerID;
                                cache.State.DeviceID = 机台marker.DeviceID;
                                await cache.WriteStateAsync();

                                Agv指令.行驶(AgvID, route, cache.State.IP);
                            }
                            else
                            {
                                //没有路线
                            }
                        }
                        else
                        {
                            //数据错误，地标没有配机台
                        }
                        #endregion
                    }
                    else
                    {
                        #region  如果有可用的待命点
                        var stayGrain = GrainFactory.GetGrain<IStayGroup>(marker.MarkerID);
                        var 可用待命点地标ID = await stayGrain.获取可用待命点ID();
                        if (可用待命点地标ID == null)
                        {
                            //没有可用的待命点
                        }
                        else
                        {
                            var routeGrain = GrainFactory.GetGrain<IRoute>(0);
                            var route = await routeGrain.获取两个地标之间的路由(marker.MarkerID, 可用待命点地标ID);
                            if (route > 0)
                            {
                                await stayGrain.锁定待命点(可用待命点地标ID, AgvID);
                                Agv指令.行驶(this.GetPrimaryKeyLong(), route, cache.State.IP);
                            }
                            else
                            {
                                //没有路线
                            }
                        }
                        #endregion
                    }
                    break;
                case MarkerEnum.机台前路口:
                    //这类情况比较复杂：正常的传输完成退回主路、还没有传输就退回主路、人为操作强制退回主路。
                    //不同工序、不同机台需要不同的处理，此处就省略代码了

                    //奇奇怪怪的实现
                    break;
            }
        }

        async Task 传输完成(MarkerInfo? marker, bool UpFull, bool DownFull)
        {
            //根据实际情况处理。常用的是告之机台传输完成
            //向管道发送“传输完成的信息”
            if (marker == null)
            {
                //传输机台的地标不存在，则不处理
                return;
            }
            if (steam != null)
            { 
                var deviceGrain = GrainFactory.GetGrain<IDeviceGroup>(marker.MarkerID);
                string deviceID = await deviceGrain.地标对应的机台标识(marker.MarkerID);

                MsgData msg = new MsgData()
                {
                    Cmd = "传输完成",
                    DeviceID = deviceID,
                };
                _ = steam.OnNextAsync(msg);
            }

        }


        async Task 可传输(MarkerInfo? marker, bool UpFull, bool DownFull)
        {
            //根据实际情况处理
            //向管道发送“AGV就位的信息” 
            if (marker == null)
            {
                //传输机台的地标不存在，则不处理
                return;
            }
            if (steam != null)
            { 
                var deviceGrain = GrainFactory.GetGrain<IDeviceGroup>(marker.MarkerID);
                string deviceID = await deviceGrain.地标对应的机台标识(marker.MarkerID);

                MsgData msg = new MsgData()
                {
                    Cmd = "AGV已就位",
                    DeviceID = deviceID,
                };
                _ = steam.OnNextAsync(msg);
            }
        }


        #endregion
    }

    #region 缓存数据定义

    [Serializable]
    public class AgvInfo
    {
       
        public string IP { get; set; } = "";
        public int X { get; set; }
        public int Y { get; set; }
        public bool UpFull { get; set; }
        public bool DownFull { get; set; }

        public int Status { get; set; }

        /// <summary>
        /// 持有的交管区ID
        /// </summary>
        public long LockAreaID { get; set; }


        /// <summary>
        /// 产线
        /// </summary>
        public int  Line { get; set; }


        /// <summary>
        /// 机台对应的下一组传输机台
        /// </summary>
        public string NextGroup { get; set; } = "";

        /// <summary>
        /// 上锁传输机台的工序或者工序分组
        /// </summary>
        public string  DeviceGroup { get; set; } = "";

        /// <summary>
        /// 上锁传输机台的地标,
        /// </summary> 
        public string DeviceMarkerID { get; set; } = "";


        /// <summary>
        /// 上锁传输机台的ID
        /// </summary>
        public string DeviceID { get; set; } = "";
    }

    #endregion
}
